define("dojo/touch", ["dojo/_base/kernel", "dojo/aspect", "dojo/dom", "dojo/dom-class", "dojo/_base/lang", "dojo/on", "dojo/has", "dojo/mouse", "dojo/domReady", "dojo/_base/window"],
function(dojo, aspect, dom, domClass, lang, on, has, mouse, domReady, win){
   // module:
   //    dojo/touch

   var ios4 = has("ios") < 5;

   // Detect if platform supports Pointer Events, and if so, the names of the events (pointerdown vs. MSPointerDown).
   var hasPointer = has("pointer-events") || has("MSPointer"),
      pointer = (function () {
         var pointer = {}; // jshint ignore:line
         for (var type in { down: 1, move: 1, up: 1, cancel: 1, over: 1, out: 1 }) { // jshint ignore:line
            pointer[type] = has("MSPointer") ?
               "MSPointer" + type.charAt(0).toUpperCase() + type.slice(1) :
               "pointer" + type;
         }
         return pointer;
      })();

   // Detect if platform supports the webkit touchstart/touchend/... events
   var hasTouch = has("touch-events");

   // Click generation variables
   var clicksInited, clickTracker, useTarget = false, clickTarget, clickX, clickY, clickDx, clickDy, clickTime;

   // Time of most recent touchstart, touchmove, or touchend event
   var lastTouch;

   function dualEvent(mouseType, touchType, pointerType){
      // Returns synthetic event that listens for both the specified mouse event and specified touch event.
      // But ignore fake mouse events that were generated due to the user touching the screen.
      if(hasPointer && pointerType){
         // IE10+: MSPointer* events are designed to handle both mouse and touch in a uniform way,
         // so just use that regardless of hasTouch.
         return function(node, listener){
            return on(node, pointerType, listener);
         };
      }else if(hasTouch){
         return function(node, listener){
            var handle1 = on(node, touchType, function(evt){
                  listener.call(this, evt);

                  // On slow mobile browsers (see https://bugs.dojotoolkit.org/ticket/17634),
                  // a handler for a touch event may take >1s to run.  That time shouldn't
                  // be included in the calculation for lastTouch.
                  lastTouch = (new Date()).getTime();
               }),
               handle2 = on(node, mouseType, function(evt){
                  if(!lastTouch || (new Date()).getTime() > lastTouch + 1000){
                     listener.call(this, evt);
                  }
               });
            return {
               remove: function(){
                  handle1.remove();
                  handle2.remove();
               }
            };
         };
      }else{
         // Avoid creating listeners for touch events on performance sensitive older browsers like IE6
         return function(node, listener){
            return on(node, mouseType, listener);
         };
      }
   }

   function marked(/*DOMNode*/ node){
      // Search for node ancestor has been marked with the dojoClick property to indicate special processing.
      // Returns marked ancestor.
      do{
         if(node.dojoClick !== undefined){ return node; }
      }while(node = node.parentNode); // jshint ignore:line
   }
   
   function doClicks(e, moveType, endType){ // jshint ignore:line
      // summary:
      //    Setup touch listeners to generate synthetic clicks immediately (rather than waiting for the browser
      //    to generate clicks after the double-tap delay) and consistently (regardless of whether event.preventDefault()
      //    was called in an event listener. Synthetic clicks are generated only if a node or one of its ancestors has
      //    its dojoClick property set to truthy. If a node receives synthetic clicks because one of its ancestors has its
      //      dojoClick property set to truthy, you can disable synthetic clicks on this node by setting its own dojoClick property
      //      to falsy.

      if(mouse.isRight(e) || mouse.isMiddle(e)){
         return;     // avoid spurious dojoclick event on IE10+; right click is just for context menu
      }

      var markedNode = marked(e.target);
      clickTracker  = !e.target.disabled && markedNode && markedNode.dojoClick; // click threshold = true, number, x/y object, or "useTarget"
      if(clickTracker){
         useTarget = (clickTracker == "useTarget"); // jshint ignore:line
         clickTarget = (useTarget?markedNode:e.target);
         if(useTarget){
            // We expect a click, so prevent any other 
            // default action on "touchpress"
            e.preventDefault();
         }
         clickX = e.changedTouches ? e.changedTouches[0].pageX - win.global.pageXOffset : e.clientX;
         clickY = e.changedTouches ? e.changedTouches[0].pageY - win.global.pageYOffset : e.clientY;
         clickDx = (typeof clickTracker == "object" ? clickTracker.x : (typeof clickTracker == "number" ? clickTracker : 0)) || 4; // jshint ignore:line
         clickDy = (typeof clickTracker == "object" ? clickTracker.y : (typeof clickTracker == "number" ? clickTracker : 0)) || 4; // jshint ignore:line

         // add move/end handlers only the first time a node with dojoClick is seen,
         // so we don't add too much overhead when dojoClick is never set.
         if(!clicksInited){
            clicksInited = true;

            function updateClickTracker(e){ // jshint ignore:line
               if(useTarget){
                  clickTracker = dom.isDescendant(
                     win.doc.elementFromPoint(
                        (e.changedTouches ? e.changedTouches[0].pageX - win.global.pageXOffset : e.clientX),
                        (e.changedTouches ? e.changedTouches[0].pageY - win.global.pageYOffset : e.clientY)),
                     clickTarget);
               }else{
                  clickTracker = clickTracker &&
                     (e.changedTouches ? e.changedTouches[0].target : e.target) == clickTarget && // jshint ignore:line
                     Math.abs((e.changedTouches ? e.changedTouches[0].pageX - win.global.pageXOffset : e.clientX) - clickX) <= clickDx &&
                     Math.abs((e.changedTouches ? e.changedTouches[0].pageY - win.global.pageYOffset : e.clientY) - clickY) <= clickDy;
               }
            }

            win.doc.addEventListener(moveType, function(e){ // jshint ignore:line
               if(mouse.isRight(e) || mouse.isMiddle(e)){
                  return;     // avoid spurious dojoclick event on IE10+; right click is just for context menu
               }
               updateClickTracker(e);
               if(useTarget){
                  // prevent native scroll event and ensure touchend is
                  // fire after touch moves between press and release.
                  e.preventDefault();
               }
            }, true);

            win.doc.addEventListener(endType, function(e){ // jshint ignore:line
               if(mouse.isRight(e) || mouse.isMiddle(e)){
                  return;     // avoid spurious dojoclick event on IE10+; right click is just for context menu
               }
               updateClickTracker(e);
               if(clickTracker){
                  clickTime = (new Date()).getTime();
                  var target = (useTarget?clickTarget:e.target);
                  if(target.tagName === "LABEL"){
                     // when clicking on a label, forward click to its associated input if any
                     target = dom.byId(target.getAttribute("for")) || target;
                  }
                  //some attributes can be on the Touch object, not on the Event:
                  //http://www.w3.org/TR/touch-events/#touch-interface
                  var src = (e.changedTouches) ? e.changedTouches[0] : e;
                  //create the synthetic event.
                  //http://www.w3.org/TR/DOM-Level-3-Events/#widl-MouseEvent-initMouseEvent
                  var clickEvt = document.createEvent("MouseEvents");
                  clickEvt._dojo_click = true;
                  clickEvt.initMouseEvent("click",
                     true, //bubbles
                     true, //cancelable
                     e.view,
                     e.detail,
                     src.screenX,
                     src.screenY,
                     src.clientX,
                     src.clientY,
                     e.ctrlKey,
                     e.altKey,
                     e.shiftKey,
                     e.metaKey,
                     0, //button
                     null //related target
                  );
                  setTimeout(function(){
                     on.emit(target, "click", clickEvt);

                     // refresh clickTime in case app-defined click handler took a long time to run
                     clickTime = (new Date()).getTime();
                  }, 0);
               }
            }, true);

            function stopNativeEvents(type){ // jshint ignore:line
               win.doc.addEventListener(type, function(e){ // jshint ignore:line
                  // Stop native events when we emitted our own click event.  Note that the native click may occur
                  // on a different node than the synthetic click event was generated on.  For example,
                  // click on a menu item, causing the menu to disappear, and then (~300ms later) the browser
                  // sends a click event to the node that was *underneath* the menu.  So stop all native events
                  // sent shortly after ours, similar to what is done in dualEvent.
                  // The INPUT.dijitOffScreen test is for offscreen inputs used in dijit/form/Button, on which
                  // we call click() explicitly, we don't want to stop this event.
                     if(!e._dojo_click &&
                        (new Date()).getTime() <= clickTime + 1000 &&
                        !(e.target.tagName == "INPUT" && domClass.contains(e.target, "dijitOffScreen"))){ // jshint ignore:line
                     e.stopPropagation();
                     e.stopImmediatePropagation && e.stopImmediatePropagation();
                     if(type == "click" && (e.target.tagName != "INPUT" || e.target.type == "radio" || e.target.type == "checkbox") // jshint ignore:line
                        && e.target.tagName != "TEXTAREA" && e.target.tagName != "AUDIO" && e.target.tagName != "VIDEO"){ // jshint ignore:line
                         // preventDefault() breaks textual <input>s on android, keyboard doesn't popup,
                         // but it is still needed for checkboxes and radio buttons, otherwise in some cases
                         // the checked state becomes inconsistent with the widget's state
                        e.preventDefault();
                     }
                  }
               }, true);
            }

            stopNativeEvents("click");

            // We also stop mousedown/up since these would be sent well after with our "fast" click (300ms),
            // which can confuse some dijit widgets.
            stopNativeEvents("mousedown");
            stopNativeEvents("mouseup");
         }
      }
   }

   var hoveredNode;

   if(hasPointer){
       // MSPointer (IE10+) already has support for over and out, so we just need to init click support
      domReady(function(){
         win.doc.addEventListener(pointer.down, function(evt){
            doClicks(evt, pointer.move, pointer.up);
         }, true);
      });
   }else if(hasTouch){
      domReady(function(){
         // Keep track of currently hovered node
         hoveredNode = win.body();  // currently hovered node

         win.doc.addEventListener("touchstart", function(evt){
               lastTouch = (new Date()).getTime();

            // Precede touchstart event with touch.over event.  DnD depends on this.
            // Use addEventListener(cb, true) to run cb before any touchstart handlers on node run,
            // and to ensure this code runs even if the listener on the node does event.stop().
            var oldNode = hoveredNode;
            hoveredNode = evt.target;
            on.emit(oldNode, "dojotouchout", {
               relatedTarget: hoveredNode,
               bubbles: true
            });
            on.emit(hoveredNode, "dojotouchover", {
               relatedTarget: oldNode,
               bubbles: true
            });

            doClicks(evt, "touchmove", "touchend"); // init click generation
         }, true);

         function copyEventProps(evt){
            // Make copy of event object and also set bubbles:true.  Used when calling on.emit().
            var props = lang.delegate(evt, {
               bubbles: true
            });

            if(has("ios") >= 6){
               // On iOS6 "touches" became a non-enumerable property, which
               // is not hit by for...in.  Ditto for the other properties below.
               props.touches = evt.touches;
               props.altKey = evt.altKey;
               props.changedTouches = evt.changedTouches;
               props.ctrlKey = evt.ctrlKey;
               props.metaKey = evt.metaKey;
               props.shiftKey = evt.shiftKey;
               props.targetTouches = evt.targetTouches;
            }

            return props;
         }

         on(win.doc, "touchmove", function(evt){
            lastTouch = (new Date()).getTime();

            var newNode = win.doc.elementFromPoint(
               evt.pageX - (ios4 ? 0 : win.global.pageXOffset), // iOS 4 expects page coords
               evt.pageY - (ios4 ? 0 : win.global.pageYOffset)
            );

            if(newNode){
               // Fire synthetic touchover and touchout events on nodes since the browser won't do it natively.
               if(hoveredNode !== newNode){
                  // touch out on the old node
                  on.emit(hoveredNode, "dojotouchout", {
                     relatedTarget: newNode,
                     bubbles: true
                  });

                  // touchover on the new node
                  on.emit(newNode, "dojotouchover", {
                     relatedTarget: hoveredNode,
                     bubbles: true
                  });

                  hoveredNode = newNode;
               }

               // Unlike a listener on "touchmove", on(node, "dojotouchmove", listener) fires when the finger
               // drags over the specified node, regardless of which node the touch started on.
               if(!on.emit(newNode, "dojotouchmove", copyEventProps(evt))){
                  // emit returns false when synthetic event "dojotouchmove" is cancelled, so we prevent the
                  // default behavior of the underlying native event "touchmove".
                  evt.preventDefault();
               }
            }
         });

         // Fire a dojotouchend event on the node where the finger was before it was removed from the screen.
         // This is different than the native touchend, which fires on the node where the drag started.
         on(win.doc, "touchend", function(evt){
               lastTouch = (new Date()).getTime();
            var node = win.doc.elementFromPoint(
               evt.pageX - (ios4 ? 0 : win.global.pageXOffset), // iOS 4 expects page coords
               evt.pageY - (ios4 ? 0 : win.global.pageYOffset)
            ) || win.body(); // if out of the screen

            on.emit(node, "dojotouchend", copyEventProps(evt));
         });
      });
   }

   //device neutral events - touch.press|move|release|cancel/over/out
   var touch = {
      press: dualEvent("mousedown", "touchstart", pointer.down),
      move: dualEvent("mousemove", "dojotouchmove", pointer.move),
      release: dualEvent("mouseup", "dojotouchend", pointer.up),
      cancel: dualEvent(mouse.leave, "touchcancel", hasPointer ? pointer.cancel : null),
      over: dualEvent("mouseover", "dojotouchover", pointer.over),
      out: dualEvent("mouseout", "dojotouchout", pointer.out),
      enter: mouse._eventHandler(dualEvent("mouseover","dojotouchover", pointer.over)),
      leave: mouse._eventHandler(dualEvent("mouseout", "dojotouchout", pointer.out))
   };

   /*=====
   touch = {
      // summary:
      //    This module provides unified touch event handlers by exporting
      //    press, move, release and cancel which can also run well on desktop.
      //    Based on http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html
      //      Also, if the dojoClick property is set to truthy on a DOM node, dojo/touch generates
      //      click events immediately for this node and its descendants (except for descendants that
      //      have a dojoClick property set to falsy), to avoid the delay before native browser click events,
      //      and regardless of whether evt.preventDefault() was called in a touch.press event listener.
      //
      // example:
      //    Used with dojo/on
      //    |  define(["dojo/on", "dojo/touch"], function(on, touch){
      //    |     on(node, touch.press, function(e){});
      //    |     on(node, touch.move, function(e){});
      //    |     on(node, touch.release, function(e){});
      //    |     on(node, touch.cancel, function(e){});
      // example:
      //    Used with touch.* directly
      //    |  touch.press(node, function(e){});
      //    |  touch.move(node, function(e){});
      //    |  touch.release(node, function(e){});
      //    |  touch.cancel(node, function(e){});
      // example:
      //    Have dojo/touch generate clicks without delay, with a default move threshold of 4 pixels
      //    |  node.dojoClick = true;
      // example:
      //    Have dojo/touch generate clicks without delay, with a move threshold of 10 pixels horizontally and vertically
      //    |  node.dojoClick = 10;
      // example:
      //    Have dojo/touch generate clicks without delay, with a move threshold of 50 pixels horizontally and 10 pixels vertically
      //    |  node.dojoClick = {x:50, y:5};
      // example:
      //    Disable clicks without delay generated by dojo/touch on a node that has an ancestor with property dojoClick set to truthy
      //    |  node.dojoClick = false;

      press: function(node, listener){
         // summary:
         //    Register a listener to 'touchstart'|'mousedown' for the given node
         // node: Dom
         //    Target node to listen to
         // listener: Function
         //    Callback function
         // returns:
         //    A handle which will be used to remove the listener by handle.remove()
      },
      move: function(node, listener){
         // summary:
         //    Register a listener that fires when the mouse cursor or a finger is dragged over the given node.
         // node: Dom
         //    Target node to listen to
         // listener: Function
         //    Callback function
         // returns:
         //    A handle which will be used to remove the listener by handle.remove()
      },
      release: function(node, listener){
         // summary:
         //    Register a listener to releasing the mouse button while the cursor is over the given node
         //    (i.e. "mouseup") or for removing the finger from the screen while touching the given node.
         // node: Dom
         //    Target node to listen to
         // listener: Function
         //    Callback function
         // returns:
         //    A handle which will be used to remove the listener by handle.remove()
      },
      cancel: function(node, listener){
         // summary:
         //    Register a listener to 'touchcancel'|'mouseleave' for the given node
         // node: Dom
         //    Target node to listen to
         // listener: Function
         //    Callback function
         // returns:
         //    A handle which will be used to remove the listener by handle.remove()
      },
      over: function(node, listener){
         // summary:
         //    Register a listener to 'mouseover' or touch equivalent for the given node
         // node: Dom
         //    Target node to listen to
         // listener: Function
         //    Callback function
         // returns:
         //    A handle which will be used to remove the listener by handle.remove()
      },
      out: function(node, listener){
         // summary:
         //    Register a listener to 'mouseout' or touch equivalent for the given node
         // node: Dom
         //    Target node to listen to
         // listener: Function
         //    Callback function
         // returns:
         //    A handle which will be used to remove the listener by handle.remove()
      },
      enter: function(node, listener){
         // summary:
         //    Register a listener to mouse.enter or touch equivalent for the given node
         // node: Dom
         //    Target node to listen to
         // listener: Function
         //    Callback function
         // returns:
         //    A handle which will be used to remove the listener by handle.remove()
      },
      leave: function(node, listener){
         // summary:
         //    Register a listener to mouse.leave or touch equivalent for the given node
         // node: Dom
         //    Target node to listen to
         // listener: Function
         //    Callback function
         // returns:
         //    A handle which will be used to remove the listener by handle.remove()
      }
   };
   =====*/

    1  && (dojo.touch = touch);

   return touch;
});